﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Web;
using iNet.IO;

namespace iNet.Cache
{
	/// <summary>
	/// 表示一個本機檔案快取區，這個類別是執行緒安全類別。
	/// </summary>
	/// <typeparam name="TKey">快取檔案的鍵值型別。</typeparam>
	public class FileCache<TKey>
	{
		#region CachedFileInfo
		class CachedFileInfo : IComparable<CachedFileInfo>
		{
			public volatile int ActiveStreamCount;
			public volatile bool HasDeletionRequest;
			public long FileSize;
			public DateTime LastAccessTime;
			public readonly TKey Key;
			public readonly SlotInfo Slot;
			public volatile CachedFileState State = CachedFileState.StandingBy;
			public readonly LinkedListNode<CachedFileInfo> UsageListNode;
			#region 建構子
			public CachedFileInfo(TKey key, SlotInfo slot, DateTime lastAccessTime)
			{
				this.Key = key;
				this.Slot = slot;
				this.LastAccessTime = lastAccessTime;
				this.UsageListNode = new LinkedListNode<CachedFileInfo>(this);
			}
			#endregion
			#region CompareTo
			public int CompareTo(CachedFileInfo other)
			{
				return DateTime.Compare(this.LastAccessTime, other.LastAccessTime);
			}
			#endregion
			#region Equals
			public override bool Equals(object obj)
			{
				CachedFileInfo other = (obj as CachedFileInfo);
				if (other != null)
					return (this.Key.Equals(other.Key) && this.LastAccessTime == other.LastAccessTime);
				return false;
			}
			#endregion
			#region GetHashCode
			public override int GetHashCode()
			{
				return ((this.Key.GetHashCode() << 16) | (this.LastAccessTime.GetHashCode() & 0xFFFF));
			}
			#endregion
		}
		#endregion
		#region CachedFileState
		enum CachedFileState
		{
			StandingBy,
			Reading,
			Writing,
		}
		#endregion
		#region CachedFileStream
		class CachedFileStream : FileStream
		{
			#region 欄位
			readonly FileCache<TKey> _Cache;
			readonly CachedFileInfo _CachedFileInfo;
			bool _IsClosed;
			#endregion
			#region 建構子
			public CachedFileStream(FileCache<TKey> cache, CachedFileInfo fileInfo, FileMode mode, FileAccess access, FileShare share)
				: base(cache.GetFilePath(fileInfo), mode, access, share)
			{
				_Cache = cache;
				_CachedFileInfo = fileInfo;
			}
			#endregion
			#region Close
			public override void Close()
			{
				bool isFirstClose;
				lock (this)
				{
					isFirstClose = !_IsClosed;
					_IsClosed = true;
				}
				base.Close();
				if (isFirstClose)
					_Cache.OnStreamClosed(_CachedFileInfo);
			}
			#endregion
		}
		#endregion
		#region DefaultKeyComparer
		class DefaultKeyComparer<TKey> : IEqualityComparer<TKey>
		{
			#region Equals
			public bool Equals(TKey x, TKey y)
			{
				if (x != null)
					return x.Equals(y);
				if (y != null)
					return y.Equals(x);
				return true;
			}
			#endregion
			#region GetHashCode
			public int GetHashCode(TKey obj)
			{
				if (obj != null)
					return obj.GetHashCode();
				return 0;
			}
			#endregion
		}
		#endregion
		#region SlotInfo
		class SlotInfo
		{
			public volatile int FileCount;
			public readonly int Index;
			public readonly LinkedListNode<SlotInfo> PriorityListNode;
			#region 建構子
			public SlotInfo(int index)
			{
				this.Index = index;
				this.PriorityListNode = new LinkedListNode<SlotInfo>(this);
			}
			#endregion
		}
		#endregion
		#region 常數
		const int MAX_SLOT_COUNT = 1000;
		const int SLOT_FILE_COUNT_THRESHOLD = 256;
		#endregion
		#region 靜態欄位
		static readonly NLog.Logger _Logger = NLog.LogManager.GetLogger("LocalFileCache");
		#endregion
		#region 欄位
		readonly Dictionary<TKey, CachedFileInfo> _CachedFileInfos;
		long _Capacity;
		readonly IEqualityComparer<TKey> _KeyComparer;
		readonly string _RootDirectory;
		readonly SlotInfo[] _SlotInfos = new SlotInfo[MAX_SLOT_COUNT];
		readonly LinkedList<SlotInfo> _SlotPriorityList = new LinkedList<SlotInfo>();
		readonly object _SyncRoot = new object();
		long _TotalFileSize;
		readonly LinkedList<CachedFileInfo> _UsageList = new LinkedList<CachedFileInfo>();
		#endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 FileCache 執行個體。
		/// </summary>
		/// <param name="rootDirectory">提供快取檔案存放的根目錄路徑。</param>
		/// <param name="capacity">快取存放區的容量上限，單位為位元組。負值表示無上限。</param>
		/// <exception cref="ArgumentException">rootDirectory 為無效的路徑。</exception>
		/// <exception cref="ArgumentNullException">rootDirectory 為 Null (Visual Basic 為 Nothing)。</exception>
		/// <exception cref="ArgumentOutOfRangeException">capacity 為零。</exception>
		public FileCache(string rootDirectory, long capacity)
			: this(rootDirectory, capacity, null)
		{ }
		/// <summary>
		/// 初始化新的 FileCache 執行個體。
		/// </summary>
		/// <param name="rootDirectory">提供快取檔案存放的根目錄路徑。</param>
		/// <param name="capacity">快取存放區的容量上限，單位為位元組。負值表示無上限。</param>
		/// <param name="comparer">提供鍵值比較的 IEqualityComparer 介面，Null (Visual Basic 為 Nothing) 表示使用預設的比較方法。</param>
		/// <exception cref="ArgumentException">rootDirectory 為無效的路徑。</exception>
		/// <exception cref="ArgumentNullException">rootDirectory 為 Null (Visual Basic 為 Nothing)。</exception>
		/// <exception cref="ArgumentOutOfRangeException">capacity 為零。</exception>
		public FileCache(string rootDirectory, long capacity, IEqualityComparer<TKey> comparer)
		{
			//確認參數
			if (rootDirectory == null)
				throw new ArgumentNullException("rootDirectory");
			if (!FileUtility.IsValidPath(rootDirectory))
				throw new ArgumentException("Invalid root directory path.");
			if (capacity == 0)
				throw new ArgumentOutOfRangeException();

			//初始化
			_RootDirectory = rootDirectory;
			_Capacity = capacity;
			_KeyComparer = (comparer != null ? comparer : new DefaultKeyComparer<TKey>());
			_CachedFileInfos = new Dictionary<TKey, CachedFileInfo>(_KeyComparer);
			this.Initialize();
		}
		#endregion
		#region Capacity
		/// <summary>
		/// 取得或設定快取存放區的容量上限，單位為位元組。負值表示無上限。
		/// </summary>
		public long Capacity
		{
			get
			{
				return _Capacity;
			}
			set
			{
				lock (_SyncRoot)
				{
					long oldCapacity = _Capacity;
					if (oldCapacity != value)
					{
						if (value == 0)
							throw new ArgumentOutOfRangeException();
						_Capacity = value;
						if (value > 0 && (value < oldCapacity || oldCapacity < 0))
							this.DeleteExtraFiles();
					}
				}
			}
		}
		#endregion
		#region Clear
		/// <summary>
		/// 清除快取存放區中所有的快取檔案。
		/// </summary>
		public void Clear()
		{
			lock (_SyncRoot)
			{
				//刪除所有檔案
				LinkedListNode<CachedFileInfo> node = _UsageList.First;
				while (node != null)
				{
					CachedFileInfo fileInfo = node.Value;
					node = node.Next;
					this.Delete(fileInfo);
				}

				//刪除存放區資料夾
				for (int i = _SlotInfos.Length - 1; i >= 0; --i)
				{
					SlotInfo slot = _SlotInfos[i];
					if (slot.FileCount == 0)
					{
						string dirPath = this.GetDirectoryPath(slot);
						try
						{
							if (Directory.Exists(dirPath))
								Directory.Delete(dirPath, true);
						}
						catch (Exception ex)
						{
							_Logger.Warn("Clear() - Fail to delete directory '" + dirPath + "'.", ex);
						}
					}
				}
			}
		}
		#endregion
		#region CompareSlots
		int CompareSlots(SlotInfo x, SlotInfo y)
		{
			if (x.FileCount <= SLOT_FILE_COUNT_THRESHOLD)
			{
				if (y.FileCount <= SLOT_FILE_COUNT_THRESHOLD)
					return (x.Index - y.Index);
				return -1;
			}
			else if (y.FileCount <= SLOT_FILE_COUNT_THRESHOLD)
				return 1;
			return (x.FileCount - y.FileCount);
		}
		#endregion
		#region Delete
		/// <summary>
		/// 將指定的檔案從快取中移除。
		/// </summary>
		/// <param name="key">快取檔案的鍵值。</param>
		public void Delete(TKey key)
		{
			lock (_SyncRoot)
			{
				CachedFileInfo fileInfo;
				if (_CachedFileInfos.TryGetValue(key, out fileInfo))
					this.Delete(fileInfo);
			}
		}
		bool Delete(CachedFileInfo fileInfo)
		{
			//確認狀態
			if (fileInfo.State != CachedFileState.StandingBy)
			{
				fileInfo.HasDeletionRequest = true;
				return false;
			}

			//刪除檔案
			try
			{
				string filePath = this.GetFilePath(fileInfo);
				if (File.Exists(filePath))
					File.Delete(filePath);
				else if (fileInfo.FileSize > 0)
				{
					_TotalFileSize -= fileInfo.FileSize;
					fileInfo.FileSize = 0;
				}
			}
			catch
			{
				return false;
			}

			//更新快取資訊
			_TotalFileSize -= fileInfo.FileSize;
			_UsageList.Remove(fileInfo.UsageListNode);
			_CachedFileInfos.Remove(fileInfo.Key);
			this.IncreaseSlotFileCount(fileInfo.Slot, -1);
			return true;
		}
		#endregion
		#region DeleteExtraFiles
		void DeleteExtraFiles()
		{
			if (_Capacity > 0 && _TotalFileSize > _Capacity && _UsageList.Count > 0)
			{
				LinkedListNode<CachedFileInfo> node = _UsageList.Last;
				while (_TotalFileSize > _Capacity && node != null)
				{
					CachedFileInfo fileInfo = node.Value;
					node = node.Previous;
					this.Delete(fileInfo);
				}
			}
		}
		#endregion
		#region GetDirectoryPath
		string GetDirectoryPath(CachedFileInfo fileInfo)
		{
			return this.GetDirectoryPath(fileInfo.Slot);
		}
		string GetDirectoryPath(SlotInfo slot)
		{
			string slotName = string.Format("{0:000}", slot.Index);
			return Path.Combine(_RootDirectory, slotName);
		}
		#endregion
		#region GetFilePath
		string GetFilePath(CachedFileInfo fileInfo)
		{
			string fileName = string.Format("{0}-{1:x}", this.ConvertKeyToFileName(fileInfo.Key), fileInfo.LastAccessTime.ToBinary());
			return Path.Combine(this.GetDirectoryPath(fileInfo), fileName);
		}
		#endregion
		#region IncreaseSlotFileCount
		void IncreaseSlotFileCount(int slotIndex, int diff)
		{
			SlotInfo slot = _SlotInfos[slotIndex];
			if (slot == null)
			{
				slot = new SlotInfo(slotIndex);
				_SlotInfos[slotIndex] = slot;
				_SlotPriorityList.AddFirst(slot.PriorityListNode);
			}
			this.IncreaseSlotFileCount(slot, diff);
		}
		void IncreaseSlotFileCount(SlotInfo slot, int diff)
		{
			//更新檔案數量
			slot.FileCount += diff;
			if (slot.FileCount < 0)
				slot.FileCount = 0;

			//更新優先權
			LinkedListNode<SlotInfo> node = slot.PriorityListNode;
			bool moveBackward = (node.Previous == null || node.Next == null || this.CompareSlots(slot, node.Next.Value) > 0);
			if (moveBackward)
			{
				while (node.Next != null && this.CompareSlots(slot, node.Next.Value) > 0)
				{
					LinkedListNode<SlotInfo> nextNode = node.Next;
					_SlotPriorityList.Remove(node);
					_SlotPriorityList.AddAfter(nextNode, node);
				}
			}
			else
			{
				while (node.Previous != null && this.CompareSlots(slot, node.Previous.Value) < 0)
				{
					LinkedListNode<SlotInfo> prevNode = node.Next;
					_SlotPriorityList.Remove(node);
					_SlotPriorityList.AddBefore(prevNode, node);
				}
			}
		}
		#endregion
		#region Initialize
		void Initialize()
		{
			//建立根目錄
			bool hasRootDirectory = true;
			try
			{
				FileUtility.CreateDirectory(_RootDirectory);
			}
			catch (Exception ex)
			{
				hasRootDirectory = false;
				_Logger.Error("Initialize() - Fail to create root directory '" + _RootDirectory + "'.", ex);
			}

			//建立儲存區資訊
			for (int i = _SlotInfos.Length - 1; i >= 0; --i)
				_SlotInfos[i] = new SlotInfo(i);

			//掃描檔案
			if (hasRootDirectory)
			{
				try
				{
					foreach (string path in Directory.EnumerateFileSystemEntries(_RootDirectory))
					{
						if (Directory.Exists(path))
						{
							int slotIndex;
							string dirName = Path.GetFileName(path);
							if (dirName.Length == 3 && int.TryParse(dirName, out slotIndex))
								this.InitializeSlot(_SlotInfos[slotIndex]);
							else
							{
								_Logger.Warn("Initialize() - Delete unknown directory '" + path + "'.");
								try
								{
									Directory.Delete(path, true);
								}
								catch (Exception ex)
								{
									_Logger.Error("Initialize() - Fail to delete unknown directory '" + path + "'.", ex);
								}
							}
						}
						else
						{
							_Logger.Warn("Initialize() - Delete unknown file '" + path + "'.");
							try
							{
								File.Delete(path);
							}
							catch (Exception ex)
							{
								_Logger.Error("Initialize() - Fail to delete unknown file '" + path + "'.", ex);
							}
						}
					}
				}
				catch (Exception ex)
				{
					_Logger.Error("Initialize() - Fail to scan root directory '" + _RootDirectory + "'.", ex);
				}
			}

			//排序檔案使用順序
			CachedFileInfo[] fileInfos = _CachedFileInfos.Values.ToArray();
			Array.Sort<CachedFileInfo>(fileInfos);
			for (int i = fileInfos.Length - 1; i >= 0; --i)
				_UsageList.AddLast(fileInfos[i].UsageListNode);

			//排序儲存區優先順序
			SlotInfo[] slots = (SlotInfo[])_SlotInfos.Clone();
			Array.Sort<SlotInfo>(slots, this.CompareSlots);
			for (int i = slots.Length - 1; i >= 0; --i)
				_SlotPriorityList.AddFirst(slots[i].PriorityListNode);

			//刪除多餘檔案
			this.DeleteExtraFiles();
		}
		#endregion
		#region InitializeSlot
		void InitializeSlot(SlotInfo slot)
		{
			string dirPath = this.GetDirectoryPath(slot);
			try
			{
				foreach (string path in Directory.EnumerateFileSystemEntries(dirPath))
				{
					if (File.Exists(path))
					{
						CachedFileInfo fileInfo = null;
						try
						{
							//取得存取時間
							string fileName = Path.GetFileName(path);
							int charIndex = fileName.LastIndexOf('-');
							long binaryDateTime = 0;
							bool hasLastAccessTime = (charIndex > 0 && long.TryParse(fileName.Substring(charIndex + 1), System.Globalization.NumberStyles.HexNumber, null, out binaryDateTime));

							//取得鍵值
							TKey key = default(TKey);
							bool hasKey = (hasLastAccessTime && this.TryConvertFileNameToKey(fileName.Substring(0, charIndex), out key));

							//建立快取檔案資訊
							fileInfo = new CachedFileInfo(key, slot, DateTime.FromBinary(binaryDateTime));
							
							//取得檔案大小
							fileInfo.FileSize = new FileInfo(path).Length;
						}
						catch (Exception ex)
						{
							fileInfo = null;
							_Logger.Error("InitializeSlot() - Fail to check file '" + path + "'.", ex);
						}
						if (fileInfo != null)
						{
							_CachedFileInfos[fileInfo.Key] = fileInfo;
							_TotalFileSize += fileInfo.FileSize;
							++slot.FileCount;
						}
						else
						{
							try
							{
								File.Delete(path);
							}
							catch (Exception ex)
							{
								_Logger.Error("InitializeSlot() - Fail to delete invalid cache file '" + path + "'.", ex);
							}
						}
					}
					else
					{
						_Logger.Warn("InitializeSlot() - Delete unknown directory '" + path + "'.");
						try
						{
							Directory.Delete(path, true);
						}
						catch (Exception ex)
						{
							_Logger.Error("InitializeSlot() - Fail to delete unknown directory '" + path + "'.", ex);
						}
					}
				}
			}
			catch (Exception ex)
			{
				_Logger.Error("InitializeSlot() - Fail to scan slot directory '" + dirPath + "'.", ex);
			}
		}
		#endregion
		#region Contains
		/// <summary>
		/// 確認指定的快取檔案是否存在於快取區中。
		/// </summary>
		/// <param name="key">快取檔案鍵值。</param>
		/// <returns>檔案是否存在於快取區中。</returns>
		public bool Contains(TKey key)
		{
			lock (_SyncRoot)
				return _CachedFileInfos.ContainsKey(key);
		}
		#endregion
		#region ConvertKeyToFileName
		/// <summary>
		/// 將指定的快取檔案鍵值轉換成對應的檔案名稱。
		/// </summary>
		/// <param name="key">指定的快取檔案鍵值。</param>
		/// <returns>對應的檔案名稱。</returns>
		protected virtual string ConvertKeyToFileName(TKey key)
		{
			if (key is DateTime)
				return ((DateTime)(object)key).ToBinary().ToString();
			return HttpUtility.UrlEncode(key.ToString());
		}
		#endregion
		#region OnStreamClosed
		void OnStreamClosed(CachedFileInfo fileInfo)
		{
			lock (_SyncRoot)
			{
				--fileInfo.ActiveStreamCount;
				if (fileInfo.ActiveStreamCount <= 0)
				{
					//更新檔案大小
					if (fileInfo.State == CachedFileState.Writing)
					{
						long oldFileSize = fileInfo.FileSize;
						try
						{
							fileInfo.FileSize = new FileInfo(this.GetFilePath(fileInfo)).Length;
						}
						catch
						{
							fileInfo.FileSize = 0;
						}
						_TotalFileSize += (fileInfo.FileSize - oldFileSize);
					}

					//更新快取檔案狀態
					fileInfo.State = CachedFileState.StandingBy;

					//刪除檔案
					if (fileInfo.HasDeletionRequest)
					{
						this.Delete(fileInfo);
						return;
					}

					//更新存取時間
					string oldFilePath = this.GetFilePath(fileInfo);
					DateTime oldAccessTime = fileInfo.LastAccessTime;
					fileInfo.LastAccessTime = DateTime.Now.ToUniversalTime();
					string newFilePath = this.GetFilePath(fileInfo);

					//更新檔案名稱
					try
					{
						File.Move(oldFilePath, newFilePath);
					}
					catch
					{
						fileInfo.LastAccessTime = oldAccessTime;
					}

					//刪除多餘的檔案
					this.DeleteExtraFiles();
				}
			}
		}
		#endregion
		#region SelectSlot
		SlotInfo SelectSlot()
		{
			if (_SlotPriorityList.Count == 0)
			{
				SlotInfo slot = new SlotInfo(0);
				_SlotInfos[0] = slot;
				_SlotPriorityList.AddFirst(slot.PriorityListNode);
			}
			return _SlotPriorityList.First.Value;
		}
		#endregion
		#region TotalFileSize
		/// <summary>
		/// 取得目前快取存放區的總檔案大小，單位為位元組。
		/// </summary>
		public long TotalFileSize
		{
			get
			{
				return _TotalFileSize;
			}
		}
		#endregion
		#region TryConvertFileNameToKey
		/// <summary>
		/// 嘗試將指定的檔案名稱轉換成對應的快取檔案鍵值。
		/// </summary>
		/// <param name="fileName">檔案名稱，不包含副檔名。</param>
		/// <param name="key">對應的快取檔案鍵值。</param>
		/// <returns>True 表示轉換成功，False 表示失敗。</returns>
		protected virtual bool TryConvertFileNameToKey(string fileName, out TKey key)
		{
			key = default(TKey);
			switch (Type.GetTypeCode(typeof(TKey)))
			{
				case TypeCode.Boolean:
					{
						bool boolValue;
						if (bool.TryParse(fileName, out boolValue))
						{
							key = (TKey)(object)boolValue;
							return true;
						}
					}
					break;
				case TypeCode.Byte:
					{
						byte byteValue;
						if (byte.TryParse(fileName, out byteValue))
						{
							key = (TKey)(object)byteValue;
							return true;
						}
					}
					break;
				case TypeCode.Char:
					{
						char charValue;
						if (char.TryParse(fileName, out charValue))
						{
							key = (TKey)(object)charValue;
							return true;
						}
					}
					break;
				case TypeCode.DateTime:
					{
						long binary;
						if (long.TryParse(fileName, out binary))
						{
							key = (TKey)(object)DateTime.FromBinary(binary);
							return true;
						}
					}
					break;
				case TypeCode.Decimal:
					{
						decimal value;
						if (decimal.TryParse(fileName, out value))
						{
							key = (TKey)(object)value;
							return true;
						}
					}
					break;
				case TypeCode.Double:
					{
						double value;
						if (double.TryParse(fileName, out value))
						{
							key = (TKey)(object)value;
							return true;
						}
					}
					break;
				case TypeCode.Int16:
					{
						short shortValue;
						if (short.TryParse(fileName, out shortValue))
						{
							key = (TKey)(object)shortValue;
							return true;
						}
					}
					break;
				case TypeCode.Int32:
					{
						int intValue;
						if (int.TryParse(fileName, out intValue))
						{
							key = (TKey)(object)intValue;
							return true;
						}
					}
					break;
				case TypeCode.Int64:
					{
						long longValue;
						if (long.TryParse(fileName, out longValue))
						{
							key = (TKey)(object)longValue;
							return true;
						}
					}
					break;
				case TypeCode.SByte:
					{
						sbyte byteValue;
						if (sbyte.TryParse(fileName, out byteValue))
						{
							key = (TKey)(object)byteValue;
							return true;
						}
					}
					break;
				case TypeCode.Single:
					{
						float value;
						if (float.TryParse(fileName, out value))
						{
							key = (TKey)(object)value;
							return true;
						}
					}
					break;
				case TypeCode.String:
					key = (TKey)(object)HttpUtility.UrlDecode(fileName);
					return true;
				case TypeCode.UInt16:
					{
						ushort shortValue;
						if (ushort.TryParse(fileName, out shortValue))
						{
							key = (TKey)(object)shortValue;
							return true;
						}
					}
					break;
				case TypeCode.UInt32:
					{
						uint intValue;
						if (uint.TryParse(fileName, out intValue))
						{
							key = (TKey)(object)intValue;
							return true;
						}
					}
					break;
				case TypeCode.UInt64:
					{
						ulong longValue;
						if (ulong.TryParse(fileName, out longValue))
						{
							key = (TKey)(object)longValue;
							return true;
						}
					}
					break;
				default:
					throw new NotImplementedException();
			}
			return false;
		}
		#endregion
		#region TryOpenRead
		/// <summary>
		/// 嘗試開啟指定鍵值的快取檔案並讀取。
		/// </summary>
		/// <param name="key">快取檔案的鍵值。</param>
		/// <param name="stream">可讀取的快取檔案資料流。</param>
		/// <returns>True 表示快取檔案存在且開啟成功，False 表示檔案不存在或開啟失敗。</returns>
		public bool TryOpenRead(TKey key, out Stream stream)
		{
			stream = null;
			lock (_SyncRoot)
			{
				//取得檔案資訊
				CachedFileInfo fileInfo;
				if (!_CachedFileInfos.TryGetValue(key, out fileInfo))
					return false;

				//確認狀態
				if (fileInfo.State == CachedFileState.Writing)
					return false;

				//開啟檔案
				try
				{
					stream = new CachedFileStream(this, fileInfo, FileMode.Open, FileAccess.Read, FileShare.Read);
				}
				catch
				{ }

				//更新快取資訊
				if (stream != null)
				{
					++fileInfo.ActiveStreamCount;
					fileInfo.State = CachedFileState.Reading;
					_UsageList.Remove(fileInfo.UsageListNode);
					_UsageList.AddFirst(fileInfo.UsageListNode);
				}
				else if (fileInfo.State == CachedFileState.StandingBy)
					this.Delete(fileInfo);

				//完成
				return (stream != null);
			}
		}
		#endregion
		#region TryOpenWrite
		/// <summary>
		/// 嘗試開啟指定鍵值的快取檔案並寫入。
		/// </summary>
		/// <param name="key">快取檔案的鍵值。</param>
		/// <param name="stream">可寫入的快取檔案資料流。</param>
		/// <returns>True 表示成功建立快取檔案且開啟成功，False 表示開啟失敗。</returns>
		public bool TryOpenWrite(TKey key, out Stream stream)
		{
			return this.TryOpenWrite(key, false, out stream);
		}
		/// <summary>
		/// 嘗試開啟指定鍵值的快取檔案並寫入。
		/// </summary>
		/// <param name="key">快取檔案的鍵值。</param>
		/// <param name="append">是否在開啟搜尋至檔案末端。</param>
		/// <param name="stream">可寫入的快取檔案資料流。</param>
		/// <returns>True 表示成功建立快取檔案且開啟成功，False 表示開啟失敗。</returns>
		public bool TryOpenWrite(TKey key, bool append, out Stream stream)
		{
			stream = null;
			lock (_SyncRoot)
			{
				//取得檔案資訊
				CachedFileInfo fileInfo;
				if (!_CachedFileInfos.TryGetValue(key, out fileInfo))
				{
					fileInfo = new CachedFileInfo(key, this.SelectSlot(), DateTime.Now.ToUniversalTime());
					try
					{
						FileUtility.CreateDirectory(this.GetDirectoryPath(fileInfo));
					}
					catch
					{
						fileInfo = null;
					}
					if (fileInfo != null)
					{
						_CachedFileInfos[key] = fileInfo;
						_UsageList.AddFirst(fileInfo.UsageListNode);
						this.IncreaseSlotFileCount(fileInfo.Slot, 1);
					}
				}
				else if (fileInfo.State != CachedFileState.StandingBy)
					return false;
				else if (!append)
				{
					_TotalFileSize -= fileInfo.FileSize;
					fileInfo.FileSize = 0;
				}

				//開啟檔案
				try
				{
					FileMode mode;
					FileAccess access;
					if (append)
					{
						mode = FileMode.Append;
						access = FileAccess.Write;
					}
					else
					{
						mode = FileMode.Create;
						access = FileAccess.ReadWrite;
					}
					stream = new CachedFileStream(this, fileInfo, mode, access, FileShare.None);
				}
				catch
				{ }

				//更新快取資訊
				if (stream != null)
				{
					++fileInfo.ActiveStreamCount;
					fileInfo.State = CachedFileState.Writing;
					_UsageList.Remove(fileInfo.UsageListNode);
					_UsageList.AddFirst(fileInfo.UsageListNode);
				}
				else if (fileInfo.State == CachedFileState.StandingBy)
					this.Delete(fileInfo);

				//完成
				return (stream != null);
			}
		}
		#endregion
		#region TryReadAllBytes
		/// <summary>
		/// 嘗試讀取指定鍵值之快取檔案中的資料。
		/// </summary>
		/// <param name="key">快取檔案鍵值。</param>
		/// <param name="bytes">檔案中的所有位元組資料。</param>
		/// <returns>True 表示快取檔案存在且讀取成功，False 表示檔案不存在或讀取失敗。</returns>
		public bool TryReadAllBytes(TKey key, out byte[] bytes)
		{
			Stream stream;
			if (this.TryOpenRead(key, out stream))
			{
				try
				{
					using (stream)
					{
						bytes = new byte[stream.Length];
						stream.Read(bytes, 0, bytes.Length);
						return true;
					}
				}
				catch (Exception ex)
				{
					_Logger.Error("TryReadAllBytes() - Fail to read file.", ex);
					bytes = null;
					return false;
				}
			}
			else
			{
				bytes = null;
				return false;
			}
		}
		#endregion
		#region TryReadAllLines
		/// <summary>
		/// 嘗試讀取指定鍵值之快取檔案中的所有字串。
		/// </summary>
		/// <param name="key">快取檔案鍵值。</param>
		/// <param name="encoding">字串編碼方式。</param>
		/// <param name="lines">檔案中的所有字串。</param>
		/// <returns>True 表示快取檔案存在且讀取成功，False 表示檔案不存在或讀取失敗。</returns>
		public bool TryReadAllLines(TKey key, Encoding encoding, out string[] lines)
		{
			Stream stream;
			if (this.TryOpenRead(key, out stream))
			{
				try
				{
					using (stream)
					{
						using (StreamReader reader = new StreamReader(stream, encoding))
						{
							List<string> lineBuffer = new List<string>();
							string line = reader.ReadLine();
							while (line != null)
							{
								lineBuffer.Add(line);
								line = reader.ReadLine();
							}
							lines = lineBuffer.ToArray();
							return true;
						}
					}
				}
				catch (Exception ex)
				{
					_Logger.Error("TryReadAllLines() - Fail to read file.", ex);
					lines = null;
					return false;
				}
			}
			else
			{
				lines = null;
				return false;
			}
		}
		#endregion
		#region TryWriteAllBytes
		/// <summary>
		/// 嘗試開啟指定鍵值的快取檔案並寫入資料。
		/// </summary>
		/// <param name="key">快取檔案鍵值。</param>
		/// <param name="bytes">欲寫入的位元組資料。</param>
		/// <returns>True 表示成功建立快取檔案且寫入成功，False 表示寫入失敗。</returns>
		public bool TryWriteAllBytes(TKey key, byte[] bytes)
		{
			if (bytes == null)
				return false;
			Stream stream;
			if (this.TryOpenWrite(key, out stream))
			{
				try
				{
					using (stream)
					{
						if (bytes.Length > 0)
							stream.Write(bytes, 0, bytes.Length);
					}
					return true;
				}
				catch (Exception ex)
				{
					_Logger.Error("TryWriteAllBytes() - Fail to write file.", ex);
					return false;
				}
			}
			return false;
		}
		#endregion
		#region TryWriteAllLines
		/// <summary>
		/// 嘗試開啟指定鍵值的快取檔案並寫入資料字串。
		/// </summary>
		/// <param name="key">快取檔案鍵值。</param>
		/// <param name="encoding">字串編碼方式。</param>
		/// <param name="lines">欲寫入的字串。</param>
		/// <returns>True 表示成功建立快取檔案且寫入成功，False 表示寫入失敗。</returns>
		public bool TryWriteAllLines(TKey key, Encoding encoding, IList<string> lines)
		{
			if (lines == null)
				return false;
			Stream stream;
			if (this.TryOpenWrite(key, out stream))
			{
				try
				{
					using (stream)
					{
						using (StreamWriter writer = new StreamWriter(stream, encoding))
						{
							for (int i = 0, count = lines.Count; i < count; ++i)
							{
								string line = lines[i];
								if (i > 0)
									writer.WriteLine();
								if (line != null)
									writer.Write(line);
							}
						}
					}
					return true;
				}
				catch (Exception ex)
				{
					_Logger.Error("TryWriteAllLines() - Fail to write file.", ex);
					return false;
				}
			}
			return false;
		}
		#endregion
	}
}
